﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Autodesk.Revit.Creation;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArch.Utils;
using TopologySearch;

namespace YJKArch.EditFloor
{
	public class FloorSearcher
	{
		public FloorSearcher(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
			this.m_ActiveView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
		}

		[DllImport("user32.dll", EntryPoint = "SendMessageA")]
		public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

		public Result SearchOutLoops(List<ThicknessOfEdgeInfo> selCurves, bool includeColumn, bool mergeEdge, ref List<List<ElementId>> edgeLoopsElementIds, ref List<List<Curve>> floorLoops)
		{
			if (selCurves.Count == 0)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			if (includeColumn)
			{
				this.AddColumnEdgeInfo(ref selCurves);
			}
			double boundaryOffset = 0.0;
			if (!this.SearchFloorOutLoops(selCurves, mergeEdge, boundaryOffset, ref edgeLoopsElementIds, ref floorLoops))
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private void AddColumnEdgeInfo(ref List<ThicknessOfEdgeInfo> selCurves)
		{
			double num = 0.05;
			BoundingBoxUV boundingBoxUV = this.CalculateEdgeInfoBoundingBox(selCurves[0]);
			for (int i = 1; i < selCurves.Count; i++)
			{
				BoundingBoxUV box = this.CalculateEdgeInfoBoundingBox(selCurves[i]);
				boundingBoxUV = Geometry.BoundingBoxesMerge(boundingBoxUV, box);
			}
			XYZ ptMin = new XYZ(boundingBoxUV.Min.U - num, boundingBoxUV.Min.V - num, 0.0);
			XYZ ptMax = new XYZ(boundingBoxUV.Max.U + num, boundingBoxUV.Max.V + num, 0.0);
			this.SearchPretreatment(ptMin, ptMax, false, true);
			selCurves.AddRange(this.m_InterimCurveList);
		}

		private void SearchPretreatment(XYZ ptMin, XYZ ptMax, bool isZerothickness, bool includeColumn)
		{
			if (includeColumn)
			{
				List<Element> list = new List<Element>();
				this.GetColumnList(ref list);
				ColumnProfileExtractor columnProfileExtractor = new ColumnProfileExtractor(this.m_Revit.Application);
				foreach (Element element in list)
				{
					FamilyInstance familyInstance = (FamilyInstance)element;
					List<EdgeInfo> columnProfile = columnProfileExtractor.GetColumnProfile(familyInstance);
					List<ThicknessOfEdgeInfo> list2 = new List<ThicknessOfEdgeInfo>();
					bool flag = false;
					foreach (EdgeInfo edgeInfo in columnProfile)
					{
						ThicknessOfEdgeInfo thicknessOfEdgeInfo = new ThicknessOfEdgeInfo(edgeInfo.ConvertTo(this.m_Revit.Application.Application), 0.0, familyInstance.Id);
						list2.Add(thicknessOfEdgeInfo);
						if (this.CanBeAddInterimCurve(ptMin, ptMax, thicknessOfEdgeInfo))
						{
							flag = true;
						}
					}
					if (flag)
					{
						this.m_InterimCurveList.AddRange(list2);
					}
				}
			}
			foreach (ThicknessOfEdgeInfo thicknessOfEdgeInfo2 in new PoleEndInterimCreator(this.m_Revit.Application).GetPoleEndInterimListByColumn(isZerothickness))
			{
				if (!thicknessOfEdgeInfo2.IsPoint)
				{
					this.AddInterimCurve(ptMin, ptMax, thicknessOfEdgeInfo2);
				}
			}
		}

		private bool CanBeAddInterimCurve(XYZ ptMin, XYZ ptMax, ThicknessOfEdgeInfo interim)
		{
			if (ptMin == null || ptMax == null)
			{
				return true;
			}
			BoundingBoxUV boundingBoxUV = this.CalculateEdgeInfoBoundingBox(interim);
			double num = Common.MMtoIntch(0.0);
			return !Geometry.LessThan(boundingBoxUV.Max.U, ptMin.X - num) && !Geometry.GreaterThan(boundingBoxUV.Min.U, ptMax.X + num) && !Geometry.LessThan(boundingBoxUV.Max.V, ptMin.Y - num) && !Geometry.GreaterThan(boundingBoxUV.Min.V, ptMax.Y + num);
		}

		private void AddInterimCurve(XYZ ptMin, XYZ ptMax, ThicknessOfEdgeInfo interim)
		{
			if (this.CanBeAddInterimCurve(ptMin, ptMax, interim))
			{
				this.m_InterimCurveList.Add(interim);
			}
		}

		private BoundingBoxUV CalculateEdgeInfoBoundingBox(ThicknessOfEdgeInfo interim)
		{
			BoundingBoxUV result;
			if (interim.IsArc)
			{
				result = Geometry.CalculateBoundingBox2D(interim.StartPoint, interim.EndPoint, interim.Center, interim.Normal, interim.Radius);
			}
			else
			{
				result = Geometry.CalculateBoundingBox2D(interim.StartPoint, interim.EndPoint);
			}
			return result;
		}

		private Result GetInitializeData(ref List<FloorType> floorTypeList, ref List<string> levelNameList, ref string message)
		{
			this.m_FloorTypeList = this.GetFloorType();
			if (this.m_FloorTypeList.Count == 0)
			{
				message = "楼板类型列表为空";
				return Autodesk.Revit.UI.Result.Failed;
			}
			foreach (FloorType item in this.m_FloorTypeList)
			{
				floorTypeList.Add(item);
			}
			this.m_LevelList = Common.GetSortLevels(this.m_Doc, double.MinValue, double.MaxValue);
			if (this.m_LevelList.Count == 0)
			{
				message = "标高列表为空";
				return Autodesk.Revit.UI.Result.Failed;
			}
			foreach (Level level in this.m_LevelList)
			{
				levelNameList.Add(level.Name);
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private List<FloorType> GetFloorType()
		{
			List<FloorType> list = new List<FloorType>();
			foreach (Element element in new FilteredElementCollector(this.m_Doc).OfClass(typeof(FloorType)).ToElements())
			{
				FloorType floorType = element as FloorType;
				if (floorType.Category.Id.IntegerValue == -2000032)
				{
					list.Add(floorType);
				}
			}
			return list;
		}

		public void GetAllRegions(List<Element> selElements, ref List<CurveArray> floorLoops, int nMinEdge = 6)
		{
			List<ThicknessOfEdgeInfo> selCurves = new List<ThicknessOfEdgeInfo>();
			this.GetAllEdgeInfos(selElements, ref selCurves);
			this.AddColumnEdgeInfo(ref selCurves);
			List<CurveArray> list = new List<CurveArray>();
			double boundaryOffset = 0.0;
			this.SearchFloorLoops(selCurves, SearchType.ST_Cells, boundaryOffset, ref list);
			foreach (CurveArray curveArray in list)
			{
				if (curveArray.Size > nMinEdge)
				{
					floorLoops.Add(curveArray);
				}
			}
		}

		private void GetAllEdgeInfos(List<Element> selElements, ref List<ThicknessOfEdgeInfo> selCurves)
		{
			double thickness = 0.0;
			foreach (Element element in selElements)
			{
				if (element.GetType() == typeof(Wall))
				{
					Wall wall = element as Wall;
					ThicknessOfEdgeInfo item = new ThicknessOfEdgeInfo((element.Location as LocationCurve).Curve, thickness, element.Id);
					ThicknessOfEdgeInfo thicknessOfEdgeInfo = new ThicknessOfEdgeInfo((element.Location as LocationCurve).Curve, wall.Width, element.Id);
					selCurves.Add(item);
					List<EdgeInfo> list = new List<EdgeInfo>();
					thicknessOfEdgeInfo.GetEndsLine(ref list);
					using (List<EdgeInfo>.Enumerator enumerator2 = list.GetEnumerator())
					{
						while (enumerator2.MoveNext())
						{
							EdgeInfo edgeInfo = enumerator2.Current;
							ThicknessOfEdgeInfo item2 = new ThicknessOfEdgeInfo(edgeInfo.StartPoint, edgeInfo.EndPoint, edgeInfo.Bulge, 0.0, element.Id);
							selCurves.Add(item2);
						}
						continue;
					}
				}
				if (element.GetType() == typeof(FamilyInstance) && element.Category.Id.IntegerValue == -2001320)
				{
					selCurves.Add(new ThicknessOfEdgeInfo((element.Location as LocationCurve).Curve, thickness, element.Id));
				}
			}
			selCurves.AddRange(this.m_InterimCurveList);
		}

		private void GetWallList(ref List<Element> elements)
		{
			foreach (Element item in new FilteredElementCollector(this.m_Doc, this.m_ActiveView.Id).OfClass(typeof(Wall)).ToElements())
			{
				elements.Add(item);
			}
		}

		private void GetGridList(ref List<Element> elements)
		{
			foreach (Element item in new FilteredElementCollector(this.m_Doc, this.m_ActiveView.Id).OfClass(typeof(Grid)).ToElements())
			{
				elements.Add(item);
			}
		}

		private void GetRefPlaneList(ref List<Element> elements)
		{
			foreach (Element item in new FilteredElementCollector(this.m_Doc, this.m_ActiveView.Id).OfClass(typeof(ReferencePlane)).ToElements())
			{
				elements.Add(item);
			}
		}

		private void GetBeamList(ref List<Element> elements)
		{
			foreach (Element item in from elem in new FilteredElementCollector(this.m_Doc, this.m_ActiveView.Id).OfClass(typeof(FamilyInstance)).ToElements()
			let familyInstance = elem as FamilyInstance
                                     where (int)familyInstance.StructuralType == 1
			select familyInstance)
			{
				elements.Add(item);
			}
		}

		private void GetColumnList(ref List<Element> elements)
		{
			foreach (Element element in from elem in new FilteredElementCollector(this.m_Doc, this.m_ActiveView.Id).OfClass(typeof(FamilyInstance)).ToElements()
			let familyInstance = elem as FamilyInstance
			where familyInstance.Category.Id.IntegerValue == -2000100 || familyInstance.Category.Id.IntegerValue == -2001330
			select familyInstance)
			{
				if ((element as FamilyInstance).Symbol.Family.Name.IndexOf("HYColumn") == -1)
				{
					elements.Add(element);
				}
			}
		}

		private List<Curve> FormatCurves(List<Curve> curveList)
		{
			List<Curve> list = new List<Curve>();
			foreach (Curve curve in curveList)
			{
				List<Curve> collection = this.FormatCurve(curve, curveList);
				list.AddRange(collection);
			}
			return list;
		}

		private List<Curve> FormatCurve(Curve curve, List<Curve> curves)
		{
			List<Curve> list = new List<Curve>();
			IntersectionResultArray intersectionResultArray = null;
			List<XYZ> list2 = new List<XYZ>();
			foreach (Curve curve2 in curves)
			{
				if (!curve.Equals(curve2))
				{
					curve.Intersect(curve2, out intersectionResultArray);
					if (intersectionResultArray != null)
					{
						for (int i = 0; i < intersectionResultArray.Size; i++)
						{
							list2.Add(intersectionResultArray.get_Item(i).XYZPoint);
						}
					}
				}
			}
			if (list2.Count <= 1)
			{
				return list;
			}
			Line line = curve as Line;
			if (line != null)
			{
				list2.Sort(new DistanceComparer(line.GetEndPoint(0)));
				for (int j = 0; j < list2.Count - 1; j++)
				{
					XYZ xyz = list2.ElementAt(j);
					XYZ xyz2 = list2.ElementAt(j + 1);
					if (!xyz.IsAlmostEqualTo(xyz2))
					{
						Line item = YJKLineEx.YJKGetBound(xyz, xyz2);
						list.Add(item);
					}
				}
			}
			Arc arc = curve as Arc;
			if (arc != null)
			{
				XYZ center = arc.Center;
				XYZ hyendPoint = arc.GetEndPoint(1);
				if (arc.Normal.IsAlmostEqualTo(XYZ.BasisZ))
				{
					hyendPoint = arc.GetEndPoint(0);
				}
				list2.Sort(new RotationComparer(hyendPoint, center));
				for (int k = 0; k < list2.Count - 1; k++)
				{
					XYZ xyz = list2.ElementAt(k);
					XYZ xyz2 = list2.ElementAt(k + 1);
					if (!xyz.IsAlmostEqualTo(xyz2))
					{
						XYZ radianPnt = Geometry.CalculatMidPoint(xyz, xyz2, center, XYZ.BasisZ);
						Arc item2 = this.m_Revit.Application.Application.CreatYJKArc(xyz, xyz2, radianPnt);
						list.Add(item2);
					}
				}
			}
			return list;
		}

		private bool SearchFloorOutLoops(List<ThicknessOfEdgeInfo> selCurves, bool mergeEdge, double boundaryOffset, ref List<List<ElementId>> edgeLoopsElementIds, ref List<List<Curve>> floorLoops)
		{
			List<Pole2D> list = new List<Pole2D>();
			foreach (ThicknessOfEdgeInfo thicknessOfEdgeInfo in selCurves)
			{
				double num = boundaryOffset + thicknessOfEdgeInfo.Thickness / 2.0;
				Pole2D item;
				if (thicknessOfEdgeInfo.IsArc)
				{
					item = Pole2D.NewStructure2DPole(thicknessOfEdgeInfo.HostElementId, thicknessOfEdgeInfo.StartPoint, thicknessOfEdgeInfo.EndPoint, thicknessOfEdgeInfo.Center, thicknessOfEdgeInfo.Normal, num, num);
				}
				else
				{
					item = Pole2D.NewStructure2DPole(thicknessOfEdgeInfo.HostElementId, thicknessOfEdgeInfo.StartPoint, thicknessOfEdgeInfo.EndPoint, num, num);
				}
				list.Add(item);
			}
			StructureLoopSearcher structureLoopSearcher = new StructureLoopSearcher(this.m_Revit);
			floorLoops = structureLoopSearcher.GetFullFloorEdgeLoops(list, mergeEdge, ref edgeLoopsElementIds, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside);
			return true;
		}

		private bool SearchFloorLoops(List<ThicknessOfEdgeInfo> selCurves, SearchType searchType, double boundaryOffset, ref List<CurveArray> floorLoops)
		{
			List<Pole2D> list = new List<Pole2D>();
			foreach (ThicknessOfEdgeInfo thicknessOfEdgeInfo in selCurves)
			{
				double num = boundaryOffset + thicknessOfEdgeInfo.Thickness / 2.0;
				if (searchType == SearchType.ST_Cells)
				{
					num = -thicknessOfEdgeInfo.Thickness / 2.0;
				}
				Pole2D item;
				if (thicknessOfEdgeInfo.IsArc)
				{
					item = Pole2D.NewStructure2DPole(thicknessOfEdgeInfo.HostElementId, thicknessOfEdgeInfo.StartPoint, thicknessOfEdgeInfo.EndPoint, thicknessOfEdgeInfo.Center, thicknessOfEdgeInfo.Normal, num, num);
				}
				else
				{
					item = Pole2D.NewStructure2DPole(thicknessOfEdgeInfo.HostElementId, thicknessOfEdgeInfo.StartPoint, thicknessOfEdgeInfo.EndPoint, num, num);
				}
				list.Add(item);
			}
			StructureLoopSearcher structureLoopSearcher = new StructureLoopSearcher(this.m_Revit);
			List<List<ElementId>> list2 = new List<List<ElementId>>();
			List<List<Curve>> list3;
			if (searchType == SearchType.ST_OutLoop)
			{
				list3 = structureLoopSearcher.GetFullFloorEdgeLoops(list, ref list2, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside);
			}
			else
			{
				list3 = structureLoopSearcher.GetCellsEdgeLoops(list, ref list2, true);
			}
			for (int i = 0; i < list3.Count; i++)
			{
				bool flag = true;
				List<Curve> list4 = list3[i];
				CurveArray curveArray = new CurveArray();
				for (int j = 0; j < list4.Count; j++)
				{
					Curve curve = list4[j];
					curveArray.Append(curve);
					if (flag)
					{
						ElementId elementId = list2[i][j];
						Element element = this.m_Doc.GetElement(elementId);
						if (element != null && element.Category.Id.IntegerValue == -2001320)
						{
							flag = false;
						}
					}
				}
				if (!flag)
				{
					floorLoops.Add(curveArray);
				}
			}
			return true;
		}

		private bool CreateFloors(List<CurveArray> floorLoops, FloorType floorType, Level level, double offset)
		{
			Transaction transaction = null;
			bool result;
			try
			{
				int num = 0;
                Autodesk.Revit.Creation.Document create = this.m_Doc.Create;
				transaction = new Transaction(this.m_Doc);
				transaction.Start("create floors");
				foreach (CurveArray curveArray in floorLoops)
				{
					try
					{
						Floor floor = create.NewFloor(curveArray, floorType, level, false);
						if (floor != null)
						{
							floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM).Set(offset);
							num++;
						}
					}
					catch (Exception)
					{
					}
				}
				transaction.Commit();
				if (num > 0)
				{
					result = true;
				}
				else
				{
					result = false;
				}
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				if (transaction != null && transaction.HasStarted() && !transaction.HasEnded())
				{
					transaction.RollBack();
				}
				result = false;
			}
			return result;
		}

		private bool GetThicknessWithFloorType(FloorType floorType, ref double thickness)
		{
			Parameter parameter = floorType.get_Parameter(BuiltInParameter.FLOOR_ATTR_THICKNESS_PARAM);
			if (parameter != null)
			{
				thickness = parameter.AsDouble();
				return true;
			}
			Parameter parameter2 = floorType.get_Parameter(BuiltInParameter.FLOOR_ATTR_DEFAULT_THICKNESS_PARAM);
			if (parameter2 != null)
			{
				thickness = parameter2.AsDouble();
				return true;
			}
			return false;
		}

		private void SetThicknessWithFloorType(FloorType floorType, double thickness)
		{
			Transaction transaction = new Transaction(this.m_Doc);
			try
			{
				transaction.Start("set thickness");
				Parameter parameter = floorType.get_Parameter(BuiltInParameter.FLOOR_ATTR_THICKNESS_PARAM);
				if (parameter != null)
				{
					parameter.Set(Common.MMtoIntch(thickness));
					transaction.Commit();
				}
				else
				{
					Parameter parameter2 = floorType.get_Parameter(BuiltInParameter.FLOOR_ATTR_DEFAULT_THICKNESS_PARAM);
					if (parameter2 != null)
					{
						parameter2.Set(Common.MMtoIntch(thickness));
						transaction.Commit();
					}
				}
			}
			catch (Exception)
			{
				transaction.RollBack();
			}
		}

		private ExternalCommandData m_Revit;

        private Autodesk.Revit.DB.Document m_Doc;

		private Autodesk.Revit.DB.View m_ActiveView;

		private List<FloorType> m_FloorTypeList;

		private List<Level> m_LevelList;

		private List<ThicknessOfEdgeInfo> m_InterimCurveList = new List<ThicknessOfEdgeInfo>();
	}
}
